package learning.algorithm.list.arraylist;

import learning.algorithm.list.AbstractList;
import learning.algorithm.list.List;

public class ArrayList<E> extends AbstractList<E> {

    private static final int DEFAULT_CAPACITY = 10;
    private Object[] elements;

    public ArrayList(int capacity) {
        if (capacity < DEFAULT_CAPACITY) {
            capacity = DEFAULT_CAPACITY;
        }
        elements = new Object[capacity];
        size = 0;
    }

    public ArrayList() {
        this(DEFAULT_CAPACITY);
    }

    @Override
    public void clear() {
        int tmpIndex = 0;
        while (tmpIndex < size) {
            elements[tmpIndex++] = null;
        }
        size = 0;
    }

    @Override
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacity(size + 1);
        int tmpIndex = size;
        while (tmpIndex > index) {
            elements[tmpIndex] = elements[--tmpIndex];
        }
        elements[index] = element;
        size++;
    }

    private void ensureCapacity(int capacity) {
        int originalCapacity = elements.length;
        if (originalCapacity >= capacity) {
            return;
        }
        int newCapacity = originalCapacity + (originalCapacity >> 1);
        Object[] newElements = new Object[newCapacity];
        int tmpIndex = 0;
        while (tmpIndex < originalCapacity) {
            newElements[tmpIndex] = elements[tmpIndex++];
        }
        elements = newElements;

    }

    @Override
    public E remove(int index) {
        rangeCheck(index);
        E oldVal = elementData(index);
        while (index < size - 1) {
            elements[index] = elements[++index];
        }
        elements[--size] = null;
        return oldVal;
    }

    @Override
    public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

    @Override
    public E set(int index, E element) {
        rangeCheck(index);
        E oldVal = elementData(index);
        elements[index] = element;
        return oldVal;
    }

    @Override
    public int indexOf(E element) {
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (elements[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(elements[i])) {
                    return i;
                }
            }
        }

        return List.ELEMENT_NOT_FOUND;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder("[");
        for (int i = 0; i < size; i++) {
            if (i != 0) {
                builder.append(", ");
            }
            builder.append(elements[i].toString());
        }
        builder.append("]");
        return "ArrayList{" +
                "elements=" + builder.toString() +
                ", size=" + size +
                '}';
    }


    private E elementData(int index) {
        return (E) elements[index];
    }
}
